<html>
<head>
<title>CpcPaChi - Version 0.04</title>
<style type="text/css">
span {
	font-family: 'Courier New';
	font-size: 10pt;
	color: #000000;
}
.sc0 {
	color: #808080;
}
.sc3 {
	color: #008080;
}
.sc4 {
	color: #FF8000;
}
.sc5 {
	font-weight: bold;
	color: #0000FF;
}
.sc7 {
	color: #808080;
}
.sc9 {
	color: #804000;
}
.sc10 {
	font-weight: bold;
	color: #000080;
}
.sc11 {
}
</style>
</head>
<body>
<h2>Un compilador sencillo paso a paso (4)</h2>

<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>


<p>Vamos a por el cuarto acercamiento. Hemos hecho que la entrada sea un poco m&aacute;s flexible, con un primer analizador l&eacute;xico. Ahora le toca el turno a la salida: la generaci&oacute;n de c&oacute;digo.<br /><br />Si hacemos que el generador de c&oacute;digo sea un m&oacute;dulo independiente (lo elegante ser&iacute;a que fuera un "objeto"; para nosotros ser&aacute; simplemente unas cuantas funciones recopiladas en una misma "unit"), conseguiremos varias ventajas:<br /><br /><ul><li>M&aacute;s legibilidad de lo que podr&iacute;amos considerar el "c&oacute;digo fuente principal".</li><li>M&aacute;s facilidad para reemplazar esa "unit" por otra cuando queramos generar c&oacute;digo para otra m&aacute;quina de destino distinta.</li></ul>Aun as&iacute;, nuestra aproximaci&oacute;n es pobre:<br /><ul><li>En un compilador "real" se suele generar primero un c&oacute;digo intermedio, parecido al ensamblador (en ocasiones puede ser algo m&aacute;s "sofisticado", como un &aacute;rbol sint&aacute;ctico, que a nosotros ahora nos pilla bastante lejos).</li><li>A este c&oacute;digo se le suele echar un primer vistazo (autom&aacute;tico) para intentar optimizar las cosas que la generaci&oacute;n de c&oacute;digo ha hecho de forma mejorable.</li><li>A continuaci&oacute;n se genera el c&oacute;digo m&aacute;quina.</li><li>Finalmente, se suele intentar optimizar tambi&eacute;n ese c&oacute;digo m&aacute;quina.<br /></li></ul><br />Claramente, nosotros no hacemos nada de eso (al menos por ahora): nos limitamos a generar c&oacute;digo m&aacute;quina en una sola pasada.<br /><br />Conociendo nuestras limitaciones, buscamos que nuestro "c&oacute;digo principal" (lo que dentro de poco llamaremos el analizador sint&aacute;ctico) sea m&aacute;s simple y legible que antes, as&iacute;:<br /><br />


<pre><code><br />program cpcPaChi;<br /><br />(* Chi - Un compilador de Pascal chiquitito para CPC<br />   Por Nacho Cabanes - Version 0.03<br />   <br />   Versiones hasta la fecha:<br />   <br />   Num.   Fecha       Cambios<br />   ---------------------------------------------------<br />   0.04  27-Mar-2008  Primera version con generador de codigo<br />                      independiente (en la unit "uPaChiG")<br /><br />   0.03  25-Mar-2008  Creado un analizador lexico independiente<br />                      (en la unit "uPaChiL")<br /><br />   0.02  23-Mar-2008  Admite CLS, LOCATE, WRITECHAR,<br />                      que lee desde fichero. Solo permite<br />                      una orden en cada linea, con formato fijo<br /><br />   0.01  21-Mar-2008  Preliminar: solo acepta CLS por teclado<br />                      y genera el codigo correspondiente<br /><br />*)<br /><br />uses<br />  upachil,  (* Analizador lexico *)<br />  upachig;  (* Generador de codigo *)<br /><br />var<br />  nombreOrigen, nombreDestino: string;<br />  orden: string;<br /><br />(* Analiza las opciones que teclee el usuario. Por ahora,<br />solo el nombre del fichero a compilar *)<br />procedure analizarOpciones;<br />begin<br />  if paramcount >= 1 then<br />    nombreOrigen := paramstr(1)<br />  else<br />    begin<br />    writeln('No se ha indicado el nombre del fichero');<br />    halt;<br />    end;<br />  abrirFicheroEntrada( nombreOrigen );<br />  abrirFicheroDestino( 'salida.bas' );<br />end;<br /><br /><br />(* Genera el codigo de destino: codigo maquina de Amstrad CPC<br />dentro de un cargador en Basic *)<br />procedure analizarUnaOrden;<br />var<br />  x, y: byte;<br />  letra: char;<br />  codError: integer;<br />begin<br />  orden := upcase(obtenerIdentificador);<br />  if orden = 'CLS' then<br />    begin<br />    genCLS;<br />    end<br />  else<br />  if orden = 'WRITECHAR' then<br />    begin<br />    leerSimbolo('(');<br />    leerSimbolo('''');<br />    letra := obtenerLetra;<br />    leerSimbolo('''');<br />    leerSimbolo(')');<br />    genWRITECHAR(letra);<br />    end<br />  else<br />  if orden = 'LOCATE' then<br />    begin<br />    leerSimbolo('(');<br />    val(obtenerEntero,x,codError);<br />    leerSimbolo(',');<br />    val(obtenerEntero,y,codError);<br />    leerSimbolo(')');<br />    genLOCATE(x,y);<br />    end<br />  else<br />  if orden = '' then<br />    begin<br />    (* orden vacia *)<br />    end<br />  else<br />    begin<br />    writeln('Error: orden no reconocida. Se esperaba: CLS, LOCATE o WRITECHAR');<br />    (*halt;*)<br />    end;<br /><br />end;<br /><br /><br />(* Cuerpo del programa *)<br />begin<br />  writeln('Compilando...');<br />  analizarOpciones;<br />  while not finFicheroEntrada do<br />    begin<br />    analizarUnaOrden;<br />    end;<br />  generarCodigoFinal;<br />  cerrarFicheroEntrada;<br />  cerrarFicheroDestino;<br />end.<br /><br /></code></pre>


<br /><br />Esto lo conseguir&iacute;amos con una analizador sint&aacute;ctico como &eacute;ste:<br /><br />

<pre><code><br />unit upachig;<br /><br />(* cpcPaChi - Un compilador de Pascal chiquitito para CPC<br />   Por Nacho Cabanes<br />   <br />   uPachiG - unidad "CodeGen" (generador de codigo)<br />   <br />   Versiones hasta la fecha:<br />   <br />   Num.   Fecha       Cambios<br />   ---------------------------------------------------<br />   0.04  27-Mar-2008  Primera version del generador de codigo:<br />                      genera codigo para CLS, LOCATE, WRITECHAR<br />                      y el codigo final del cargador BASIC<br />                      para un codigo maquina que estara dentro<br />                      de ordenes DATA.<br />*)<br /><br /><br />interface<br /><br />  function abrirFicheroDestino(nombre: string): boolean;<br />  procedure cerrarFicheroDestino;<br />  <br />  procedure genCLS;<br />  procedure genLOCATE(x,y: byte);<br />  procedure genWRITECHAR(letra: char);<br />  <br />  procedure generarCodigoFinal;<br /><br /><br />implementation<br /><br />const<br />  lineaActual: integer = 10;<br />  longitudTotal: integer = 0;<br /><br /><br />var<br />  ficheroDestino: text;<br /><br /><br />(* Abre el fichero de entrada. Devuelve FALSE si no se<br />ha podido *)<br />function abrirFicheroDestino(nombre: string): boolean;<br />begin<br />  assign( ficheroDestino, nombre );<br />  {$I-}<br />  rewrite( ficheroDestino );<br />  {$I+}<br /><br />  if ioResult <> 0 then<br />    begin<br />    abrirFicheroDestino := false;<br />    end;<br />end;<br /><br /><br />(* Generar codigo para CLS *)<br />procedure genCLS;<br />begin<br />  writeln( ficheroDestino, lineaActual,' DATA CD,6C,BB: '' CALL &BB6C - CLS' );<br />  lineaActual := lineaActual + 10;<br />  longitudTotal := longitudTotal + 3;<br />end;<br /><br />(* Generar codigo para LOCATE *)<br />procedure genLOCATE(x,y: byte);<br />begin<br />  writeln( ficheroDestino, lineaActual,' DATA 3E,',<br />    hexStr(x,2), ': '' LD A, ',x );<br />  lineaActual := lineaActual + 10;<br />  writeln( ficheroDestino, lineaActual,' DATA CD,6F,BB: '' CALL &BB6F - CURSOR COLUMN' );<br />  lineaActual := lineaActual + 10;<br />  writeln( ficheroDestino, lineaActual,' DATA 3E,',<br />    hexStr(y,2), ': '' LD A, ',y );<br />  lineaActual := lineaActual + 10;<br />  writeln( ficheroDestino, lineaActual,' DATA CD,72,BB: '' CALL &BB72 - CURSOR ROW' );<br />  lineaActual := lineaActual + 10;<br />  longitudTotal := longitudTotal + 10;<br />end;<br /><br /><br />(* Generar codigo para WRITECHAR *)<br />procedure genWRITECHAR(letra: char);<br />begin<br />  writeln( ficheroDestino, lineaActual,' DATA 3E,',<br />    hexStr(ord(letra),2), ': '' LD A, "',letra, '"' );<br />  lineaActual := lineaActual + 10;<br />  writeln( ficheroDestino, lineaActual,' DATA CD,5A,BB: '' CALL &BB5A - WRITECHAR' );<br />  lineaActual := lineaActual + 10;<br />  longitudTotal := longitudTotal + 5;<br />end;<br /><br />(* Cerrar fichero de destino (al final de la generacion) *)<br />procedure cerrarFicheroDestino;<br />begin<br />  close(ficheroDestino);<br />end;<br /><br /><br />(* Genera el codigo de destino final: cargador Basic *)<br />procedure generarCodigoFinal;<br />begin<br />  append( ficheroDestino );<br />  writeln( ficheroDestino, lineaActual,' DATA C9: '' RET' );<br />  writeln( ficheroDestino, lineaActual+10,' longitud = ',longitudTotal );<br />  writeln( ficheroDestino, lineaActual+20,' MEMORY 39999' );<br />  writeln( ficheroDestino, lineaActual+30,' FOR n=40000 TO 40000+longitud' );<br />  writeln( ficheroDestino, lineaActual+40,' READ a$:POKE n,VAL("&"+a$)' );<br />  writeln( ficheroDestino, lineaActual+50,' NEXT' );<br />  writeln( ficheroDestino, lineaActual+60,' CALL 40000' );<br />end;<br /><br /><br />begin<br />end.<br /></code></pre>


<br /><br />Por cierto... leer c&oacute;digo fuente en un Blog no especialmente c&oacute;modo. Si quieres leer esta informaci&oacute;n con sintaxis coloreada en colores, o echar un vistazo m&aacute;s detenido al fuente, o descargarlo a partir de un fichero ZIP, puedes mirar la p&aacute;gina del proyecto en Google Code:<br /><br /><a href="http://code.google.com/p/cpcpachi/">http://code.google.com/p/cpcpachi/</a></p>



<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>
</body>
</html>
